package com.lex;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Slf4j
public class Lexer {
    // Java关键字的集合
    private static final HashMap<String, TokenType> KEYWORDS = new HashMap<>();
    private List<Token> tokenList = new ArrayList<>();

    private String input;
    private int currentPosition;
    private char currentChar;
    private int currentLine;
    private int currentColumn;

    static {
        KEYWORDS.put("abstract", TokenType.ABSTRACT);
        KEYWORDS.put("assert", TokenType.ASSERT);
        KEYWORDS.put("boolean", TokenType.BOOLEAN);
        KEYWORDS.put("break", TokenType.BREAK);
        KEYWORDS.put("byte", TokenType.BYTE);
        KEYWORDS.put("case", TokenType.CASE);
        KEYWORDS.put("catch", TokenType.CATCH);
        KEYWORDS.put("char", TokenType.CHAR);
        KEYWORDS.put("class", TokenType.CLASS);
        KEYWORDS.put("const", TokenType.CONST);
        KEYWORDS.put("continue", TokenType.CONTINUE);
        KEYWORDS.put("default", TokenType.DEFAULT);
        KEYWORDS.put("do", TokenType.DO);
        KEYWORDS.put("double", TokenType.DOUBLE);
        KEYWORDS.put("else", TokenType.ELSE);
        KEYWORDS.put("enum", TokenType.ENUM);
        KEYWORDS.put("extends", TokenType.EXTENDS);
        KEYWORDS.put("final", TokenType.FINAL);
        KEYWORDS.put("finally", TokenType.FINALLY);
        KEYWORDS.put("float", TokenType.FLOAT);
        KEYWORDS.put("for", TokenType.FOR);
        KEYWORDS.put("if", TokenType.IF);
        KEYWORDS.put("implements", TokenType.IMPLEMENTS);
        KEYWORDS.put("import", TokenType.IMPORT);
        KEYWORDS.put("instanceof", TokenType.INSTANCEOF);
        KEYWORDS.put("int", TokenType.INT);
        KEYWORDS.put("interface", TokenType.INTERFACE);
        KEYWORDS.put("long", TokenType.LONG);
        KEYWORDS.put("native", TokenType.NATIVE);
        KEYWORDS.put("new", TokenType.NEW);
        KEYWORDS.put("package", TokenType.PACKAGE);
        KEYWORDS.put("private", TokenType.PRIVATE);
        KEYWORDS.put("protected", TokenType.PROTECTED);
        KEYWORDS.put("public", TokenType.PUBLIC);
        KEYWORDS.put("return", TokenType.RETURN);
        KEYWORDS.put("short", TokenType.SHORT);
        KEYWORDS.put("static", TokenType.STATIC);
        KEYWORDS.put("strictfp", TokenType.STRICTFP);
        KEYWORDS.put("super", TokenType.SUPER);
        KEYWORDS.put("switch", TokenType.SWITCH);
        KEYWORDS.put("synchronized", TokenType.SYNCHRONIZED);
        KEYWORDS.put("this", TokenType.THIS);
        KEYWORDS.put("throw", TokenType.THROW);
        KEYWORDS.put("throws", TokenType.THROWS);
        KEYWORDS.put("transient", TokenType.TRANSIENT);
        KEYWORDS.put("try", TokenType.TRY);
        KEYWORDS.put("void", TokenType.VOID);
        KEYWORDS.put("volatile", TokenType.VOLATILE);
        KEYWORDS.put("while", TokenType.WHILE);
        KEYWORDS.put("null", TokenType.NULL);
        KEYWORDS.put("true",TokenType.BOOLEAN_LITERAL);
        KEYWORDS.put("false",TokenType.BOOLEAN_LITERAL);
        KEYWORDS.put("@",TokenType.AT);
        KEYWORDS.put("goto",TokenType.GOTO);
    }


    public Lexer(String input) {
        this.input = input;
        this.currentPosition = 0;
        this.currentChar = this.input.charAt(0);
        this.currentLine = 1;//当前token的开始行数
        this.currentColumn = 1;//上一个token的结束的字符所在列数
    }

    public void init() {
        this.currentPosition = 0;
        this.currentChar = this.input.charAt(0);
        this.currentLine = 1;//当前token的开始行数
        this.currentColumn = 1;//上一个token的结束的字符所在列数
    }

    private void advance() {
        if (log.isDebugEnabled()) {
            log.debug("-------------------------------------------------------");
            log.debug("当前位置: " + this.currentPosition);
            log.debug(" str: " + this.input.charAt(this.currentPosition));
            log.debug(" value: " + (int) this.input.charAt(this.currentPosition));
            log.debug("-------------------------------------------------------");

        }
        if (this.currentChar != '\n' && this.currentChar != '\r'){
            this.currentColumn++;
        }
        this.currentPosition++;
        if (this.currentPosition > this.input.length() - 1) {
            this.currentChar = '\0';
        } else {
            this.currentChar = this.input.charAt(this.currentPosition);
        }
    }

    private void skipWhitespace() {
        while (this.currentChar != '\0' && Character.isWhitespace(this.currentChar)) {
            if (this.currentChar == '\n'){
                this.currentLine++;
                this.currentColumn = 1;
            }
            this.advance();
        }
    }

    public boolean isJavaKeyword(String s) {
        return KEYWORDS.get(s) != null;
    }

    private String parseInteger() {
        StringBuilder result = new StringBuilder();
        while (this.currentChar != '\0' && Character.isDigit(this.currentChar)) {
            result.append(this.currentChar);
            this.advance();
        }
        return result.toString();
    }

    private Token parseIdentifier() {
        int start = this.currentPosition;
        StringBuilder result = new StringBuilder();
        while (this.currentChar != '\0' && (Character.isLetterOrDigit(this.currentChar) || this.currentChar == '_')) {
            result.append(this.currentChar);
            this.advance();
        }
        String tmpStr = result.toString();
        if (isJavaKeyword(tmpStr)) {
            return new Token(KEYWORDS.get(tmpStr), tmpStr, start, this.currentPosition);
        } else {
            return new Token(TokenType.IDENTIFIER, tmpStr, start, this.currentPosition);
        }
    }

    private Token parseComments() {
        int start = this.currentPosition;
        StringBuilder result = new StringBuilder();
        result.append(this.currentChar);
        this.advance();
        if (this.currentChar != '\0' && !Character.isWhitespace(this.currentChar) && this.currentChar == '/') {
            //读取遇到换行符以外的所有
            do {
                result.append(this.currentChar);
                this.advance();
            }
            while (this.currentChar != '\0' && this.currentChar != '\r' && this.currentChar != '\n');
        } else if (this.currentChar != '\0' && !Character.isWhitespace(this.currentChar) && this.currentChar == '*') {
            //读取包括*/之前所有内容
            while (true) {
                result.append(this.currentChar);
                this.advance();
                if (this.currentChar != '\0' && this.currentChar == '*') {
                    if (this.input.charAt(this.currentPosition + 1) == '/') {
                        result.append(this.currentChar);
                        this.advance();
                        break;
                    }
                }
            }
            result.append(this.currentChar);
            this.advance();
        } else {
            //只有一个独苗 不是//  也不是 /* 那就是除操作符
            return new Token(TokenType.SLASH, result.toString(), start, this.currentPosition);
        }
        return new Token(TokenType.COMMENTS, result.toString(), start, this.currentPosition);

    }

    private Token parseStringLiteral() {
        int start = this.currentPosition;
        StringBuilder result = new StringBuilder();
        do {
            result.append(this.currentChar);
            this.advance();
        } while (this.currentChar != '"');
        result.append(this.currentChar);
        this.advance();
        return new Token(TokenType.STRING_LITERAL, result.toString(), start, this.currentPosition);
    }

    private Token parseCharLiteral() {
        int start = this.currentPosition;
        StringBuilder result = new StringBuilder();
        do {
            result.append(this.currentChar);
            this.advance();
        } while (this.currentChar != '\0' && this.currentChar != '\'');
        //'a' 'b' 'c' '1' '4' ...
        result.append(this.currentChar);
        this.advance();
        return new Token(TokenType.CHARACTER_LITERAL, result.toString(), start, this.currentPosition);
    }

    public Token getNextToken() throws Exception {
        Token createToken = null;
        while (this.currentChar != '\0') {
            if (Character.isWhitespace(this.currentChar)) {//跳过空格
                this.skipWhitespace();
                continue;
            } else if (this.currentChar == '\'') {
                createToken = this.parseCharLiteral();
            } else if (this.currentChar == '=') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.EQUAL, "=", start, this.currentPosition);
            } else if (this.currentChar == '+') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.PLUS, "+", start, this.currentPosition);
            } else if (this.currentChar == '-') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.MINUS, "-", start, this.currentPosition);
            } else if (this.currentChar == '*') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.STAR, "*", start, this.currentPosition);
            } else if (this.currentChar == '"') {//处理字符串
                createToken = this.parseStringLiteral();
            } else if (this.currentChar == '/') {
                createToken = this.parseComments();//单行注释 多行注释 文档注释 除操作符
            } else if (this.currentChar == '(') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.LEFT_PAREN, "(", start, this.currentPosition);
            } else if (this.currentChar == '{') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.LEFT_BRACE, "{", start, this.currentPosition);
            } else if (this.currentChar == '[') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.LEFT_BRACKET, "[", start, this.currentPosition);
            } else if (Character.isDigit(this.currentChar)) {//数字
                int start = this.currentPosition;
                createToken = new Token(TokenType.INTEGER_LITERAL, this.parseInteger(), start, this.currentPosition);
            } else if (Character.isLetter(this.currentChar) || this.currentChar == '_') {
                createToken = this.parseIdentifier();//字符串  包括关键字
            } else if (this.currentChar == '.') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.DOT, ".", start, this.currentPosition);
            } else if (this.currentChar == ',') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.COMMA, ",", start, this.currentPosition);
            } else if (this.currentChar == ']') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.RIGHT_BRACKET, "]", start, this.currentPosition);
            } else if (this.currentChar == ')') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.RIGHT_PAREN, ")", start, this.currentPosition);
            } else if (this.currentChar == ';') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.SEMICOLON, ";", start, this.currentPosition);
            } else if (this.currentChar == '}') {
                int start = this.currentPosition;
                this.advance();
                createToken = new Token(TokenType.RIGHT_BRACE, "}", start, this.currentPosition);
            } else {
                throw new Exception("Invalid character: " + this.currentChar);
            }
            return updatePosition(createToken);
        }
        return updatePosition(new Token(TokenType.EOF, "", this.currentPosition, this.currentPosition));
    }

    private Token updatePosition(Token token) {
        int tmpCol =  this.currentColumn;
        token.setStartLine(this.currentLine);
        token.setEndColumn(this.currentColumn-1);
        String tokenValue = token.getValue();
        if(tokenValue.contains("\r\n")){
            for (int i = 0; i < tokenValue.length(); i++) {
                if (tokenValue.charAt(i) == '\n') {
                    this.currentLine++;
                    this.currentColumn=1;
                }else this.currentColumn++;
            }
            token.setEndColumn(this.currentColumn-1);

        }
        String replaceValue = tokenValue.replace("\r\n", "");
        for (int i = 0; i < replaceValue.length(); i++) {
            tmpCol--;
        }
        token.setEndLine(this.currentLine);
        token.setStartColumn(tmpCol);
        this.tokenList.add(token);
        if (token.getType().equals(TokenType.EOF)){
            token.setEndColumn(this.currentColumn);
        }
        return token;
    }


    public List<Token> getTokenList() {
        return tokenList;
    }

}
